use std::{collections::{VecDeque,HashMap}, sync::Mutex};

use bevy::{ecs::system::Resource, prelude::{Deref, DerefMut}, time::{Stopwatch, Timer, TimerMode}};
use nalgebra::Vector2;
use serde::{Deserialize, Serialize};
use soukoban::{direction::Direction, Map};

use crate::{board::Board, database, solve::solver::{LowerBoundMethod, Solver, Strategy}, utils::PushState};

#[derive(Serialize,Deserialize,Default)]
pub struct SolverConfig{
    pub strategy: Strategy,
    pub lower_bound_method: LowerBoundMethod,
}

#[derive(Resource,Serialize,Deserialize)]
pub struct Config{
    pub player_move_speed: f32,
    pub even_square_shades: f32,
    pub volume: f64,
    pub instant_move: bool,
    pub auto_switch_to_next_unsolved_level: bool,
    pub solver: SolverConfig,
}
impl Default for Config {
    fn default() -> Self {
        Self { 
            player_move_speed: 0.1, 
            even_square_shades: 0.1, 
            volume: 0.5, 
            instant_move: false, 
            auto_switch_to_next_unsolved_level: true, 
            solver: SolverConfig::default(), 
        }
    }
}

#[derive(Resource,Deref)]
pub struct Database(pub Mutex<database::Database>);

#[derive(Resource,Deref,DerefMut)]
pub struct LevelId(pub u64);

#[derive(Resource)]
pub struct PlayerMovement {
    pub directions: VecDeque<Direction>,
    pub timer: Timer,
}

impl PlayerMovement {
    pub fn new(player_move_speed: f32) -> Self {
        Self { 
            directions: VecDeque::new(), 
            timer: Timer::from_seconds(player_move_speed, 
                TimerMode::Repeating), 
        }
    }
}

#[derive(Resource,Default)]
pub enum AutoMoveState{
    #[default]
    Player,
    Box {
        position: Vector2<i32>,
        paths: HashMap<PushState,Vec<Vector2<i32>>>,
    }
}

#[derive(Resource)]
pub struct SolverState{
    pub solver: Mutex<Solver>,
    pub stopwatch: Stopwatch,
    pub origin_board: Board,
}

impl Default for SolverState {
    fn default() -> Self {
        Self { 
            solver: Mutex::new(Solver::new(
                Map::with_dimensions(Vector2::new(0, 0)), 
                Strategy::default(),
                LowerBoundMethod::default()
            )), 
            stopwatch: Stopwatch::new(), 
            origin_board: Board::with_map(Map::with_dimensions(Vector2::new(0, 0)))
        }
    }
}